package com.csap.framework.oauth2.security.webflux;

import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.reactor.context.SaReactorHolder;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.util.SaTokenConsts;
import com.alibaba.fastjson.JSON;
import com.csap.framework.extension.result.ResultParam;
import com.csap.framework.extension.result.ResultUtil;
import com.csap.framework.oauth2.FindUserService;
import com.csap.framework.oauth2.client.Oauth2ClientProperties;
import com.csap.framework.oauth2.exception.Oauth2Exception;
import com.csap.framework.oauth2.security.Oauth2SecurityProperties;
import com.csap.framework.oauth2.security.SecurityFilter;
import com.csap.framework.oauth2.security.user.UserContext;
import com.csap.framework.util.CollectionsUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

/**
 * spring webflux 过滤器
 *
 * @Author ycf
 * @Date 2021/8/30 2:48 下午
 * @Version 1.0
 */
@Order(SaTokenConsts.ASSEMBLY_ORDER)
@Slf4j
public class SecurityWebfluxFilter extends SecurityFilter<SecurityWebfluxFilter> implements WebFilter {
    public SecurityWebfluxFilter(SaOAuth2Config saOAuth2Config, FindUserService findUserService, Oauth2SecurityProperties oauth2SecurityProperties, Oauth2ClientProperties oauth2ClientProperties) {
        super(saOAuth2Config, findUserService, oauth2SecurityProperties, oauth2ClientProperties);
    }

    @NotNull
    @Override
    public Mono<Void> filter(@NotNull ServerWebExchange exchange, @NotNull WebFilterChain chain) {
        // ---------- 全局认证处理
        try {
            // 写入全局上下文 (同步)
            SaReactorSyncHolder.setContent(exchange);
            doOptions(exchange.getResponse().getHeaders());
            // 执行全局过滤器
            filterMapping(exchange.getRequest().getMethod().name());
            // 执行
            return chain.filter(exchange).subscriberContext(ctx -> {
                // 写入全局上下文 (异步)
                ctx = ctx.put(SaReactorHolder.CONTEXT_KEY, exchange);
                return ctx;
            }).doFinally(r -> {
                // 清除上下文
                SaReactorSyncHolder.clearContent();
                UserContext.remove();
            });
        } catch (Throwable e) {
            log.error(e.getMessage(), e);
            // 1. 获取异常处理策略结果
            ResultParam<?> resultParam = ResultUtil.serviceError();
            if (e instanceof SaTokenException) {
                exchange.getResponse().setStatusCode((((SaTokenException) e).getHttpStatus()));
                resultParam = ResultUtil.error(((SaTokenException) e).getHttpStatus().value() + "", e.getMessage(), ((SaTokenException) e).getHttpStatus().getReasonPhrase());
            }
            if (e instanceof Oauth2Exception) {
                resultParam = ResultUtil.error(((Oauth2Exception) e).exceptionType().getHttpStatus().value() + "", e.getMessage(), ((Oauth2Exception) e).exceptionType().getHttpStatus().getReasonPhrase());
            }
            // 2. 写入输出流
            if (exchange.getResponse().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE) == null) {
                exchange.getResponse().getHeaders().set(HttpHeaders.CONTENT_TYPE, "text/plain; charset=utf-8");
            }
            return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(JSON.toJSONString(resultParam).getBytes())));
        }
    }

    /**
     * 处理全局想要的头部信息
     *
     * @param response response信息
     */
    protected void doOptions(HttpHeaders response) {
        if (CollectionsUtil.isNotEmpty(oauth2SecurityProperties.getHeaders())) {
            oauth2SecurityProperties.getHeaders().forEach(response::set);
        }
    }
}
